STM32第三章-系统时钟配置
学习系统时钟之前先问大家一个问题?
这是一个跑马灯的程序,为什么我们没有在主函数中配置系统时钟,却可以正常的执行流水灯代码呢?
我想大多数人都会说,已经配置了系统时钟,不需要在主函数中调用了。如果你回答不上来或者不清楚系统时钟是怎样配置的就好好看下文了。至此引出系统时钟的概念。
所有数字计算机系统都是由某种形式的震荡时钟电路驱动的。这种电路被称为系统的“脉搏”,是系统正确运行的关键。如果振荡器失灵,系统将完全无法运行,如果振荡器运行不规律,系统执行的所有时间有关的计算都会有误差。
所有微控制器的启动流程都不通用。由于硬件的复杂性,必须运行一段由厂家定义的短小的“复位程序”来使硬件处于一种正确的状态,然后再开始执行用户程序。运行这个复位程序需要时间并且要求微控制器的振荡器已经运行。
我这里以STM32F429为例,其他的芯片的原理都是一样的。STM32F429主板上有两个无源晶振。第一个是主晶振,频率为8MHz,经过内部PLL倍频到168MHz,作为CPU内核的时钟使用。第二个晶振频率为32.768kHz,用于内部RTC电路。第二个晶振不是必须的,当需要使用STM32内部RTC时钟功能时才需要安装32.768k晶振。
CPU的内核时钟可以高达180MHz,但并不是说我们就必须要设置为最高180MHZ。你可以设置为小于等于180MHZ都可以。但是CPU内部的外设,比如ADC,GPIO,部分TIM是达不到这么高的速度的。CPU的时钟管理电路有好几个分频器,不同速度的外设给定不同的时钟。
主时钟选择的是HSE高速外部时钟。
系统时钟 SYSCLK =PLLCLK= 168MHz
AHB总线时钟HCLK = SYSCLK=168MHz
APB1外设时钟PCLK1 = HCLK /4=42MHz
APB2外设时钟PCLK2 = HCLK /2=84MHz
时钟树单纯讲理论的话会比较枯燥,如果选取一条主线,并辅以代码,先主后次讲解的话会很容易,而且记忆还更深刻。我们这里选取库函数时钟系统时钟函数SetSysClock()这个函数在system_stm32f4xx.c中,以这个函数的编写流程来讲解时钟树,这个函数也是我们用库的时候默认的系统时钟设置函数。该函数的功能是利用HSE把时钟设置为:
系统时钟 SYSCLK =PLLCLK= 168MHz
AHB总线时钟HCLK = SYSCLK=168MHz
APB1外设时钟PCLK1 = HCLK /4=42MHz
APB2外设时钟PCLK2 = HCLK /2=84MHz
SYSCLK=180M,APB1=90M,APB2=180M吗?
下面我们来看时钟树:
在 STM32F429 中,有 5 个最重要的时钟源,按照上图的顺序为LSI、LSE、 HSE、HSL、PLL。H开头的是高速时钟、L开头的是低速时钟。其中 PLL分为三个时钟源,分别为主 PLL 和 I2S 部分专用 PLLI2S 和 SAI 部分专用 PLLSAI。其中 HSE 和 LSE 是外部时钟源就是从外部通过接晶振的方式获取时钟源(就是需要外界晶振),其他的是内部时钟源(不需要外接晶振)。下面我们看看 STM32F429 的这 5 个时钟源,我们讲解顺序是按图中红圈标示的顺序:
这里我们着重看看主 PLL 时钟第一个高速时钟输出 PLLP 的计算方法。是主 PLL 的时钟图。
主 PLL 时钟的时钟源要先经过一个分频系数为 M 的分频器,然后经过倍频系数为 N 的倍频器出来之后还需要经过一个分频系数为 P(第一个输出 PLLP)或者 Q(第二个输出 PLLQ)的分频器分频之后,最后才生成最终的主 PLL 时钟。例如我们的外部晶振选择 8MHz。同时我们设置相应的分频器 M=8,倍频器倍频系数 N=336,分频器分频系数 P=2,那么主 PLL 生成的第一个输出高速时钟 PLLP 为:8MHZ/8*336/2=168
PLL=8MHz * N/ (M*P)=8MHz* 336 /(8*2) = 168MHz
助记:因为2大于1,所以APB2外设时钟也一定要大于APB1外设时钟。
具体的实现请看下图:
单纯地讲解系统的时钟框图的,请看具体的数据手册,我们这里只是简单的介绍这个系统时钟是怎么来的。
PLL_M=25、PLL_N=360、PLL_P=2。
那么我的系统时钟SYSCLK就是:
PLL=25MHz * N/ (M*P)=25MHz* 330 /(25*2) = 180MHz
如果我们选择HSE为PLL时钟源,同时SYSCLK时钟源为PLL,那么SYSCLK时钟为 180MHz。HCLK=SYSCLK=PLLCLK=180M,只需要设置AHB Prescaler分频因子为1。PCLK1=HCLK/2=90M,只需要设置AHB1 Prescaler分频因子为2。PCLK2=HCLK=180M,只需要设置AHB2 Prescaler分频因子为1就可以了。
代码分析
至此我们已经大概的了解到了系统时钟是怎么一回事。现在我们来分析具体的代码。不然只分析没代码,学起来效果不好。
还记得我在前面我提到的问题吗?其实在按下单片机上面的复位按键之后,系统会这些首先执行启动代码的里面的程序。
我们打开startup_stm32f429_439xx.s文件。当然我这里以F4为例,其他的也都是一样的。会看到在系统复位时候在执行main函数之前先执行了SystemInit函数。那我们现在看看SystemInit函数中都有啥。鼠标放在SystemInit函数上面右键选择Go To Defintion of SystemInit
就会跳转到system_stm32f4xx.c文件中的SystemInit函数上来了。
下面来一行一行分析:
RCC->CR |= (uint32_t)0x00000001;
第595行:复位RCC寄存器的时钟配置CFGR寄存器,全为0。
第601行:复位RCC寄存器的配置PLLCFGR寄存器。具体的操作可以看下面对应的寄存器位值。
第604行:复位RCC寄存器的CR寄存器。
第615行:SetSysClock();这个函数主要是配置AHB、AHB1、AHB2的时钟频率。鼠标放在SetSysClock上面右键选择Go To Defintion of SetSysClock。
到这里系统时钟就全部配置完成了。在这两个函数中有很多的宏定义,在看代码的时候一定要注意是操作了哪一位,在对应为去找STM32fx的中文参考手册就容易理解多了。
要会看宏定义:
# define RCC_CR_HSEON ((uint32_t)0x00010000) 0x0001 0000
就是把RCC寄存器的CR寄存器的HSEON置位1。根据 0x0001 0000可以知道HSEON处于CR寄存器的第16位,这一点很重要。
最后来一个总结,在我们按下开发板上面的复位按键之后,系统从启动文件中先执行SystemInit函数,在SystemInit函数中配置了PLL锁相环的各个分频因子,得到了系统时钟。在SystemInit函数中又调用了SetSysClock()函数,来配置AHB、AHB1、AHB2的时钟频率。
现在我们系统时钟配置你会了吗?需要注意的是,我们写在程序的时候一般不需要配置时钟,因为我们一般用的是被人写好的工程模板,直接拿来用就可以了。但是还是需要了解,时钟树的知识比较难以理解,相信大家看上面的内容都会配置了吧!